﻿
//
//  Copyright (c) 2011 Extreme: Paweł Huryn (lead), Jakub Domański
//

using System;
using System.Data.Linq;
using System.Linq;
using System.Reflection;

namespace Extreme.SPFramework.DomainModel
{
    /// <summary>
    /// Base entity for LINQ
    /// </summary>
    public abstract class LinqEntityBase : EntityBase
    {
        /// <summary>
        /// Not suported
        /// </summary>
        public override int? ListId
        {
            get { throw new NotSupportedException(); }
            set { throw new NotSupportedException(); }
        }

        // Attached to context
        protected bool? Attached
        {
            get;
            private set;
        }

        /// <summary>
        /// Dattaches entity from data context
        /// </summary>
        public virtual void Detach()
        {
            foreach (var eventName in new string[] { "PropertyChanging", "PropertyChanged" })
            {
                var info = this.GetType().GetEvent(eventName);
                var field = this.GetType().GetField(eventName, BindingFlags.NonPublic | BindingFlags.Instance);
                field.SetValue(this, null);
            }

            Attached = false;
        }

        /// <summary>
        /// Attaches modified entity to context or declares to insert entity on submit)
        /// </summary>
        public virtual void Attach(DataContext context)
        {
            if (Attached.HasValue && Attached.Value)
                return;

            if (UniqueId.GetValueOrDefault() == Guid.Empty)
                context.GetTable(this.GetType()).InsertOnSubmit(this);
            else
                context.GetTable(this.GetType()).Attach(this, true);

            Attached = true;
        }

        /// <summary>
        /// Detaches entity
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="entity"></param>
        /// <returns></returns>
        protected static EntityRef<TEntity> Detach<TEntity>(EntityRef<TEntity> entity)
            where TEntity : LinqEntityBase
        {
            if (!entity.HasLoadedOrAssignedValue || entity.Entity == null)
                return new System.Data.Linq.EntityRef<TEntity>();
            entity.Entity.Detach();
            return new System.Data.Linq.EntityRef<TEntity>(entity.Entity);
        }

        /// <summary>
        /// Detaches entity set
        /// </summary>
        protected static EntitySet<TEntity> Detach<TEntity>(EntitySet<TEntity> set, Action<TEntity> onAdd, Action<TEntity> onRemove)
            where TEntity : LinqEntityBase
        {
            if (set == null || !set.HasLoadedOrAssignedValues)
                return new System.Data.Linq.EntitySet<TEntity>(onAdd, onRemove);

            // copy list and detach all entities
            var list = set.ToList();
            list.ForEach(t => t.Detach());

            var newSet = new System.Data.Linq.EntitySet<TEntity>(onAdd, onRemove);
            newSet.Assign(list);
            return newSet;
        }

        /// <summary>
        /// Detaches delay loaded property
        /// </summary>
        protected static System.Data.Linq.Link<T> Detach<T>(System.Data.Linq.Link<T> value)
        {
            if (!value.HasLoadedOrAssignedValue)
                return default(System.Data.Linq.Link<T>);

            return new System.Data.Linq.Link<T>(value.Value);
        }
    }
}
